try:
    from sage.all import *
    Ecc = EllipticCurve
    PR = PolynomialRing
    PRs = lambda *args, **kwargs: PolynomialRing(*args, **kwargs, implementation="generic")
except:
    print('Warning:Sage not found!')
import requests
from Crypto.Util.number import long_to_bytes as l2b,bytes_to_long as b2l
from gmpy2 import gcd,is_prime,iroot
from uuid import uuid4
from pathlib import Path
LIB_DIR = Path(__file__).parent.resolve()

# 以后有钱再试试怎么搞这个功能
def crypto_getflag():
    with open(f"{LIB_DIR}/deepseekapi") as f:
        apikey = f.read().strip()
    apikey = ""
    url = "https://api.deepseek.com/chat/completions"
    headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {apikey}"
    }

    data = {
        "model": "deepseek-reasoner", 
        # R1模型: deepseek-reasoner
        # V3模型: deepseek-chat
        "messages": [
        {"role": "system", "content": "你是一个密码学专家, 需要破解以下密码题目, 并给出exp.py"},
        {"role": "user", "content": f"{task}"}
        ],
        "stream": False # 关闭流式传输
    }

# ===============板子型函数======================
def mul_small_roots(f, bounds, m=1, d=None):
    '''
    todo: 还有一点未来
    多元coppersmith攻击的求小根函数.
    e.g.
    P = PRs(Zmod(n), ("x", "y"))
    x, y = P.gens()
    f = e * (hint + x) + y - 1
    ans, k = small_roots(f, [2 ** 180, 2 ** 100], 1, 3)[0]
    '''
    import itertools
    if not d:
        d = f.degree()
    R = f.base_ring()
    N = R.cardinality()
    f /= f.coefficients().pop(0)
    f = f.change_ring(ZZ)
    G = Sequence([], f.parent())
    for i in range(m + 1):
        base = N ** (m - i) * f ** i
        for shifts in itertools.product(range(d), repeat=f.nvariables()):
            g = base * prod(map(power, f.variables(), shifts))
            G.append(g)
    B, monomials = G.coefficients_monomials()
    monomials = vector(monomials)
    factors = [monomial(*bounds) for monomial in monomials]
    for i, factor in enumerate(factors):
        B.rescale_col(i, factor)
    B = B.dense_matrix().LLL()
    B = B.change_ring(QQ)
    for i, factor in enumerate(factors):
        B.rescale_col(i, 1 / factor)
    H = Sequence([], f.parent().change_ring(QQ))
    for h in filter(None, B * monomials):
        H.append(h)
        I = H.ideal()
        if I.dimension() == -1:
            H.pop()
        elif I.dimension() == 0:
            roots = []
            for root in I.variety(ring=ZZ):
                root = tuple(R(root[var]) for var in f.variables())
                roots.append(root)
            return roots
    return []

def L_linear_p_q(h1, h2, n, B=10):
    '''
    当p, q线性存在线性关系时使用LLL爆破p,q
    e.g.
        h1 = p + b * q
        h2 = a * p + q
        n = p * q
    p,q = L_linear_p_q(h1, h2, n)
    '''
    for i in range(2**B):
        for j in range(2**B):
            mat = matrix(ZZ,
                [
                    [1, 0, 0, 2**B*h1],
                    [0, 1, 0, 2**B*h2],
                    [0, 0, 2**(1024-B), h1*i+h2*j-h1*h2],
                    [0, 0, 0, n]
                ]
            )
            mat[:, -1:] *= n
            res = mat.LLL()[0]
            p = 2**B * abs(res[0]) + i
            if (n % p == 0) and isPrime(p):
                q = n//p
                return p,q
    print("未找到p,q")
    return 2,2

def common_prime_g(N, g):
    '''
    共素数rsa中已知g的常规情况:
        p = 2ga + 1
        q = 2gb + 1
        h = p*q = 2gab + a + b
        N = 2gh + 1
    即共素数g的情况下, 可以直接求出p,q.
    '''
    gamma=g.bit_length()/N.bit_length()
    cbits = ceil(Integer(N).nbits() * (0.5 - 2 * gamma))
    M = (N - 1) // (2 * g)
    u = M // (2 * g)
    v = M - 2 * g * u
    GF = Zmod(N)
    x = GF.random_element()
    y = x ** (2 * g)
    c = bsgs(y, y ** u, (Integer(2**(cbits-1)), Integer(2**(cbits+1))), operation='*')
    ab = u - c
    apb = v + 2 * g * c
    PR = ZZ['x']
    x = PR.gen()
    f = x ** 2 - apb * x + ab
    a = f.roots()
    if a:
        a, b = a[0][0], a[1][0]
        p = 2 * g * a + 1
        q = 2 * g * b + 1
        assert p * q == N
        return p, q

# ===============功能型函数======================
def factordb(n):
    '''
    todo: 有点丑陋, 可能无效, 如果有重复可能会无效
    先凑合着用, 以后慢慢实现断网功能.
    '''
    s=[]
    url="http://factordb.com/api?query="+str(n)
    r = requests.get(url)
    factors=r.json()['factors']
    for f in factors:
            for i in range(f[1]):
                    s.append(int(f[0]))
    return s

def get_phin(n):
    '''
    todo: 实现快速获取phin(n)的功能, 基于factordb,
    以后打算添加sage的factor实现断网生成.
    '''
    pq = factordb(n)
    phin = 1
    for i in pq:
        phin *= i - 1
    return phin

def get_n(e, x):
    '''
    e^n = x, 快速求解n
    '''
    e,x = int(e),int(x)
    n = 10
    while pow(e, n) < x:
        n *= 10
    for i in range(len(str(n))-1):
        delta = (pow(10, len(str(n))-2 - i))
        while pow(e, n) > x:
            n -= delta
        if pow(e, n) == x:
            return n,True
        n += delta
    return n, False

def flag(flag = 'flag'):
    '''
    用于出题中快速生成flag的函数
    '''
    return flag + '{' + str(uuid4()) + '}'

